home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / ai_cmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  40.4 KB  |  1,559 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3.  
  4. /*****************************************************************************
  5.  * name:        ai_cmd.c
  6.  *
  7.  * desc:        Quake3 bot AI
  8.  *
  9.  * $Archive: /source/code/game/ai_cmd.c $
  10.  * $Author: Raduffy $ 
  11.  * $Revision: 5 $
  12.  * $Modtime: 1/14/00 5:27p $
  13.  * $Date: 1/14/00 5:35p $
  14.  *
  15.  *****************************************************************************/
  16.  
  17. #include "g_local.h"
  18. #include "botlib.h"
  19. #include "be_aas.h"
  20. #include "be_ea.h"
  21. #include "be_ai_char.h"
  22. #include "be_ai_chat.h"
  23. #include "be_ai_gen.h"
  24. #include "be_ai_goal.h"
  25. #include "be_ai_move.h"
  26. #include "be_ai_weap.h"
  27. //
  28. #include "ai_main.h"
  29. #include "ai_dmq3.h"
  30. #include "ai_chat.h"
  31. #include "ai_cmd.h"
  32. #include "ai_dmnet.h"
  33. #include "ai_team.h"
  34. //
  35. #include "chars.h"                //characteristics
  36. #include "inv.h"                //indexes into the inventory
  37. #include "syn.h"                //synonyms
  38. #include "match.h"                //string matching types and vars
  39.  
  40.  
  41. #ifdef DEBUG
  42. /*
  43. ==================
  44. BotPrintTeamGoal
  45. ==================
  46. */
  47. void BotPrintTeamGoal(bot_state_t *bs) {
  48.     char netname[MAX_NETNAME];
  49.     float t;
  50.  
  51.     ClientName(bs->client, netname, sizeof(netname));
  52.     t = bs->teamgoal_time - trap_AAS_Time();
  53.     switch(bs->ltgtype) {
  54.         case LTG_TEAMHELP:
  55.         {
  56.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t);
  57.             break;
  58.         }
  59.         case LTG_TEAMACCOMPANY:
  60.         {
  61.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t);
  62.             break;
  63.         }
  64.         case LTG_GETFLAG:
  65.         {
  66.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t);
  67.             break;
  68.         }
  69.         case LTG_RUSHBASE:
  70.         {
  71.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t);
  72.             break;
  73.         }
  74.         case LTG_RETURNFLAG:
  75.         {
  76.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t);
  77.             break;
  78.         }
  79.         case LTG_DEFENDKEYAREA:
  80.         {
  81.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t);
  82.             break;
  83.         }
  84.         case LTG_GETITEM:
  85.         {
  86.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t);
  87.             break;
  88.         }
  89.         case LTG_KILL:
  90.         {
  91.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t);
  92.             break;
  93.         }
  94.         case LTG_CAMP:
  95.         case LTG_CAMPORDER:
  96.         {
  97.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t);
  98.             break;
  99.         }
  100.         case LTG_PATROL:
  101.         {
  102.             BotAI_Print(PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t);
  103.             break;
  104.         }
  105.         default:
  106.         {
  107.             if (bs->ctfroam_time > trap_AAS_Time()) {
  108.                 t = bs->ctfroam_time - trap_AAS_Time();
  109.                 BotAI_Print(PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t);
  110.             }
  111.             else {
  112.                 BotAI_Print(PRT_MESSAGE, "%s: I've got a regular goal\n", netname);
  113.             }
  114.         }
  115.     }
  116. }
  117. #endif //DEBUG
  118.  
  119. /*
  120. ==================
  121. BotGetItemTeamGoal
  122.  
  123. FIXME: add stuff like "upper rocket launcher"
  124. "the rl near the railgun", "lower grenade launcher" etc.
  125. ==================
  126. */
  127. int BotGetItemTeamGoal(char *goalname, bot_goal_t *goal) {
  128.     int i;
  129.  
  130.     if (!strlen(goalname)) return qfalse;
  131.     i = -1;
  132.     do {
  133.         i = trap_BotGetLevelItemGoal(i, goalname, goal);
  134.         if (i > 0) {
  135.             //do NOT defend dropped items
  136.             if (goal->flags & GFL_DROPPED)
  137.                 continue;
  138.             return qtrue;
  139.         }
  140.     } while(i > 0);
  141.     return qfalse;
  142. }
  143.  
  144. /*
  145. ==================
  146. BotGetMessageTeamGoal
  147. ==================
  148. */
  149. int BotGetMessageTeamGoal(bot_state_t *bs, char *goalname, bot_goal_t *goal) {
  150.     bot_waypoint_t *cp;
  151.  
  152.     if (BotGetItemTeamGoal(goalname, goal)) return qtrue;
  153.  
  154.     cp = BotFindWayPoint(bs->checkpoints, goalname);
  155.     if (cp) {
  156.         memcpy(goal, &cp->goal, sizeof(bot_goal_t));
  157.         return qtrue;
  158.     }
  159.     return qfalse;
  160. }
  161.  
  162. /*
  163. ==================
  164. BotGetTime
  165. ==================
  166. */
  167. float BotGetTime(bot_match_t *match) {
  168.     bot_match_t timematch;
  169.     char timestring[MAX_MESSAGE_SIZE];
  170.     float t;
  171.  
  172.     //if the matched string has a time
  173.     if (match->subtype & ST_TIME) {
  174.         //get the time string
  175.         trap_BotMatchVariable(match, TIME, timestring, MAX_MESSAGE_SIZE);
  176.         //match it to find out if the time is in seconds or minutes
  177.         if (trap_BotFindMatch(timestring, &timematch, MTCONTEXT_TIME)) {
  178.             if (timematch.type == MSG_FOREVER) {
  179.                 t = 99999999;
  180.             }
  181.             else {
  182.                 trap_BotMatchVariable(&timematch, TIME, timestring, MAX_MESSAGE_SIZE);
  183.                 if (timematch.type == MSG_MINUTES) t = atof(timestring) * 60;
  184.                 else if (timematch.type == MSG_SECONDS) t = atof(timestring);
  185.                 else t = 0;
  186.             }
  187.             //if there's a valid time
  188.             if (t > 0) return trap_AAS_Time() + t;
  189.         }
  190.     }
  191.     return 0;
  192. }
  193.  
  194. /*
  195. ==================
  196. FindClientByName
  197. ==================
  198. */
  199. int FindClientByName(char *name) {
  200.     int i;
  201.     char buf[MAX_INFO_STRING];
  202.     static int maxclients;
  203.  
  204.     if (!maxclients)
  205.         maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
  206.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  207.         ClientName(i, buf, sizeof(buf));
  208.         if (!Q_stricmp(buf, name)) return i;
  209.     }
  210.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  211.         ClientName(i, buf, sizeof(buf));
  212.         if (stristr(buf, name)) return i;
  213.     }
  214.     return -1;
  215. }
  216.  
  217. /*
  218. ==================
  219. FindEnemyByName
  220. ==================
  221. */
  222. int FindEnemyByName(bot_state_t *bs, char *name) {
  223.     int i;
  224.     char buf[MAX_INFO_STRING];
  225.     static int maxclients;
  226.  
  227.     if (!maxclients)
  228.         maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
  229.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  230.         if (BotSameTeam(bs, i)) continue;
  231.         ClientName(i, buf, sizeof(buf));
  232.         if (!Q_stricmp(buf, name)) return i;
  233.     }
  234.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  235.         if (BotSameTeam(bs, i)) continue;
  236.         ClientName(i, buf, sizeof(buf));
  237.         if (stristr(buf, name)) return i;
  238.     }
  239.     return -1;
  240. }
  241.  
  242. /*
  243. ==================
  244. NumPlayersOnSameTeam
  245. ==================
  246. */
  247. int NumPlayersOnSameTeam(bot_state_t *bs) {
  248.     int i, num;
  249.     char buf[MAX_INFO_STRING];
  250.     static int maxclients;
  251.  
  252.     if (!maxclients)
  253.         maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
  254.  
  255.     num = 0;
  256.     for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
  257.         trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING);
  258.         if (strlen(buf)) {
  259.             if (BotSameTeam(bs, i+1)) num++;
  260.         }
  261.     }
  262.     return num;
  263. }
  264.  
  265. /*
  266. ==================
  267. TeamPlayIsOn
  268. ==================
  269. */
  270. int BotGetPatrolWaypoints(bot_state_t *bs, bot_match_t *match) {
  271.     char keyarea[MAX_MESSAGE_SIZE];
  272.     int patrolflags;
  273.     bot_waypoint_t *wp, *newwp, *newpatrolpoints;
  274.     bot_match_t keyareamatch;
  275.     bot_goal_t goal;
  276.  
  277.     newpatrolpoints = NULL;
  278.     patrolflags = 0;
  279.     //
  280.     trap_BotMatchVariable(match, KEYAREA, keyarea, MAX_MESSAGE_SIZE);
  281.     //
  282.     while(1) {
  283.         if (!trap_BotFindMatch(keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA)) {
  284.             trap_EA_SayTeam(bs->client, "what do you say?");
  285.             BotFreeWaypoints(newpatrolpoints);
  286.             bs->patrolpoints = NULL;
  287.             return qfalse;
  288.         }
  289.         trap_BotMatchVariable(&keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE);
  290.         if (!BotGetMessageTeamGoal(bs, keyarea, &goal)) {
  291.             //BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL);
  292.             //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  293.             BotFreeWaypoints(newpatrolpoints);
  294.             bs->patrolpoints = NULL;
  295.             return qfalse;
  296.         }
  297.         //create a new waypoint
  298.         newwp = BotCreateWayPoint(keyarea, goal.origin, goal.areanum);
  299.         //add the waypoint to the patrol points
  300.         newwp->next = NULL;
  301.         for (wp = newpatrolpoints; wp && wp->next; wp = wp->next);
  302.         if (!wp) {
  303.             newpatrolpoints = newwp;
  304.             newwp->prev = NULL;
  305.         }
  306.         else {
  307.             wp->next = newwp;
  308.             newwp->prev = wp;
  309.         }
  310.         //
  311.         if (keyareamatch.subtype & ST_BACK) {
  312.             patrolflags = PATROL_LOOP;
  313.             break;
  314.         }
  315.         else if (keyareamatch.subtype & ST_REVERSE) {
  316.             patrolflags = PATROL_REVERSE;
  317.             break;
  318.         }
  319.         else if (keyareamatch.subtype & ST_MORE) {
  320.             trap_BotMatchVariable(&keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE);
  321.         }
  322.         else {
  323.             break;
  324.         }
  325.     }
  326.     //
  327.     if (!newpatrolpoints || !newpatrolpoints->next) {
  328.         trap_EA_SayTeam(bs->client, "I need more key points to patrol\n");
  329.         BotFreeWaypoints(newpatrolpoints);
  330.         newpatrolpoints = NULL;
  331.         return qfalse;
  332.     }
  333.     //
  334.     BotFreeWaypoints(bs->patrolpoints);
  335.     bs->patrolpoints = newpatrolpoints;
  336.     //
  337.     bs->curpatrolpoint = bs->patrolpoints;
  338.     bs->patrolflags = patrolflags;
  339.     //
  340.     return qtrue;
  341. }
  342.  
  343. /*
  344. ==================
  345. BotAddressedToBot
  346. ==================
  347. */
  348. int BotAddressedToBot(bot_state_t *bs, bot_match_t *match) {
  349.     char addressedto[MAX_MESSAGE_SIZE];
  350.     char netname[MAX_MESSAGE_SIZE];
  351.     char name[MAX_MESSAGE_SIZE];
  352.     char botname[128];
  353.     int client;
  354.     bot_match_t addresseematch;
  355.  
  356.     trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  357.     client = ClientFromName(netname);
  358.     if (client < 0) return qfalse;
  359.     if (!BotSameTeam(bs, client)) return qfalse;
  360.     //if the message is addressed to someone
  361.     if (match->subtype & ST_ADDRESSED) {
  362.         trap_BotMatchVariable(match, ADDRESSEE, addressedto, sizeof(addressedto));
  363.         //the name of this bot
  364.         ClientName(bs->client, botname, 128);
  365.         //
  366.         while(trap_BotFindMatch(addressedto, &addresseematch, MTCONTEXT_ADDRESSEE)) {
  367.             if (addresseematch.type == MSG_EVERYONE) {
  368.                 return qtrue;
  369.             }
  370.             else if (addresseematch.type == MSG_MULTIPLENAMES) {
  371.                 trap_BotMatchVariable(&addresseematch, TEAMMATE, name, sizeof(name));
  372.                 if (strlen(name)) {
  373.                     if (stristr(botname, name)) return qtrue;
  374.                     if (stristr(bs->subteam, name)) return qtrue;
  375.                 }
  376.                 trap_BotMatchVariable(&addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE);
  377.             }
  378.             else {
  379.                 trap_BotMatchVariable(&addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE);
  380.                 if (strlen(name)) {
  381.                     if (stristr(botname, name)) return qtrue;
  382.                     if (stristr(bs->subteam, name)) return qtrue;
  383.                 }
  384.                 break;
  385.             }
  386.         }
  387.         //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto);
  388.         //trap_EA_Say(bs->client, buf);
  389.         return qfalse;
  390.     }
  391.     else {
  392.         //make sure not everyone reacts to this message
  393.         if (random() > (float ) 1.0 / (NumPlayersOnSameTeam(bs)-1)) return qfalse;
  394.     }
  395.     return qtrue;
  396. }
  397.  
  398. /*
  399. ==================
  400. BotGPSToPosition
  401. ==================
  402. */
  403. int BotGPSToPosition(char *buf, vec3_t position) {
  404.     int i, j = 0;
  405.     int num, sign;
  406.  
  407.     for (i = 0; i < 3; i++) {
  408.         num = 0;
  409.         while(buf[j] == ' ') j++;
  410.         if (buf[j] == '-') {
  411.             j++;
  412.             sign = -1;
  413.         }
  414.         else {
  415.             sign = 1;
  416.         }
  417.         while (buf[j]) {
  418.             if (buf[j] >= '0' && buf[j] <= '9') {
  419.                 num = num * 10 + buf[j] - '0';
  420.                 j++;
  421.             }
  422.             else {
  423.                 j++;
  424.                 break;
  425.             }
  426.         }
  427.         BotAI_Print(PRT_MESSAGE, "%d\n", sign * num);
  428.         position[i] = (float) sign * num;
  429.     }
  430.     return qtrue;
  431. }
  432.  
  433. /*
  434. ==================
  435. BotMatch_HelpAccompany
  436. ==================
  437. */
  438. void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) {
  439.     int client, other, areanum;
  440.     char teammate[MAX_MESSAGE_SIZE], netname[MAX_MESSAGE_SIZE];
  441.     char itemname[MAX_MESSAGE_SIZE];
  442.     bot_match_t teammatematch;
  443.     aas_entityinfo_t entinfo;
  444.  
  445.     if (!TeamPlayIsOn()) return;
  446.     //if not addressed to this bot
  447.     if (!BotAddressedToBot(bs, match)) return;
  448.     //get the team mate name
  449.     trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
  450.     //get the client to help
  451.     if (trap_BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) &&
  452.             //if someone asks for him or herself
  453.             teammatematch.type == MSG_ME) {
  454.         //get the netname
  455.         trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  456.         client = ClientFromName(netname);
  457.         other = qfalse;
  458.     }
  459.     else {
  460.         //asked for someone else
  461.         client = FindClientByName(teammate);
  462.         //if this is the bot self
  463.         if (client == bs->client) {
  464.             other = qfalse;
  465.         }
  466.         else if (!BotSameTeam(bs, client)) {
  467.             //FIXME: say "I don't help the enemy"
  468.             return;
  469.         }
  470.         else {
  471.             other = qtrue;
  472.         }
  473.     }
  474.     //if the bot doesn't know who to help (FindClientByName returned -1)
  475.     if (client < 0) {
  476.         if (other) BotAI_BotInitialChat(bs, "whois", teammate, NULL);
  477.         else BotAI_BotInitialChat(bs, "whois", netname, NULL);
  478.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  479.         return;
  480.     }
  481.     //don't help or accompany yourself
  482.     if (client == bs->client) {
  483.         return;
  484.     }
  485.     //
  486.     bs->teamgoal.entitynum = -1;
  487.     BotEntityInfo(client, &entinfo);
  488.     //if info is valid (in PVS)
  489.     if (entinfo.valid) {
  490.         areanum = BotPointAreaNum(entinfo.origin);
  491.         if (areanum && trap_AAS_AreaReachability(areanum)) {
  492.             bs->teamgoal.entitynum = client;
  493.             bs->teamgoal.areanum = areanum;
  494.             VectorCopy(entinfo.origin, bs->teamgoal.origin);
  495.             VectorSet(bs->teamgoal.mins, -8, -8, -8);
  496.             VectorSet(bs->teamgoal.maxs, 8, 8, 8);
  497.         }
  498.     }
  499.     //if no teamgoal yet
  500.     if (bs->teamgoal.entitynum < 0) {
  501.         //if near an item
  502.         if (match->subtype & ST_NEARITEM) {
  503.             //get the match variable
  504.             trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname));
  505.             //
  506.             if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
  507.                 //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
  508.                 //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  509.                 return;
  510.             }
  511.         }
  512.     }
  513.     //
  514.     if (bs->teamgoal.entitynum < 0) {
  515.         if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL);
  516.         else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);
  517.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  518.         return;
  519.     }
  520.     //the team mate
  521.     bs->teammate = client;
  522.     //last time the team mate was assumed visible
  523.     bs->teammatevisible_time = trap_AAS_Time();
  524.     //set the time to send a message to the team mates
  525.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  526.     //get the team goal time
  527.     bs->teamgoal_time = BotGetTime(match);
  528.     //set the ltg type
  529.     if (match->type == MSG_HELP) {
  530.         bs->ltgtype = LTG_TEAMHELP;
  531.         if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_HELP_TIME;
  532.     }
  533.     else {
  534.         bs->ltgtype = LTG_TEAMACCOMPANY;
  535.         if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_ACCOMPANY_TIME;
  536.         bs->formation_dist = 3.5 * 32;        //3.5 meter
  537.         bs->arrive_time = 0;
  538.     }
  539. #ifdef DEBUG
  540.     BotPrintTeamGoal(bs);
  541. #endif //DEBUG
  542. }
  543.  
  544. /*
  545. ==================
  546. BotMatch_DefendKeyArea
  547. ==================
  548. */
  549. void BotMatch_DefendKeyArea(bot_state_t *bs, bot_match_t *match) {
  550.     char itemname[MAX_MESSAGE_SIZE];
  551.  
  552.     if (!TeamPlayIsOn()) return;
  553.     //if not addressed to this bot
  554.     if (!BotAddressedToBot(bs, match)) return;
  555.     //get the match variable
  556.     trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname));
  557.     //
  558.     if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
  559.         //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
  560.         //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  561.         return;
  562.     }
  563.     //set the time to send a message to the team mates
  564.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  565.     //set the ltg type
  566.     bs->ltgtype = LTG_DEFENDKEYAREA;
  567.     //get the team goal time
  568.     bs->teamgoal_time = BotGetTime(match);
  569.     //set the team goal time
  570.     if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_DEFENDKEYAREA_TIME;
  571.     //away from defending
  572.     bs->defendaway_time = 0;
  573. #ifdef DEBUG
  574.     BotPrintTeamGoal(bs);
  575. #endif //DEBUG
  576. }
  577.  
  578. /*
  579. ==================
  580. BotMatch_GetItem
  581. ==================
  582. */
  583. void BotMatch_GetItem(bot_state_t *bs, bot_match_t *match) {
  584.     char itemname[MAX_MESSAGE_SIZE];
  585.  
  586.     if (!TeamPlayIsOn()) return;
  587.     //if not addressed to this bot
  588.     if (!BotAddressedToBot(bs, match)) return;
  589.     //get the match variable
  590.     trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname));
  591.     //
  592.     if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
  593.         //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
  594.         //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  595.         return;
  596.     }
  597.     //set the time to send a message to the team mates
  598.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  599.     //set the ltg type
  600.     bs->ltgtype = LTG_GETITEM;
  601.     //set the team goal time
  602.     bs->teamgoal_time = trap_AAS_Time() + TEAM_GETITEM_TIME;
  603. #ifdef DEBUG
  604.     BotPrintTeamGoal(bs);
  605. #endif //DEBUG
  606. }
  607.  
  608. /*
  609. ==================
  610. BotMatch_Camp
  611. ==================
  612. */
  613. void BotMatch_Camp(bot_state_t *bs, bot_match_t *match) {
  614.     int client, areanum;
  615.     char netname[MAX_MESSAGE_SIZE];
  616.     char itemname[MAX_MESSAGE_SIZE];
  617.     aas_entityinfo_t entinfo;
  618.  
  619.     if (!TeamPlayIsOn()) return;
  620.     //if not addressed to this bot
  621.     if (!BotAddressedToBot(bs, match)) return;
  622.     //
  623.     trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  624.     //asked for someone else
  625.     client = FindClientByName(netname);
  626.     //if there's no valid client with this name
  627.     if (client < 0) {
  628.         BotAI_BotInitialChat(bs, "whois", netname, NULL);
  629.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  630.         return;
  631.     }
  632.     //get the match variable
  633.     trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname));
  634.     //in CTF it could be the base
  635.     if (match->subtype & ST_THERE) {
  636.         //camp at the spot the bot is currently standing
  637.         bs->teamgoal.entitynum = bs->entitynum;
  638.         bs->teamgoal.areanum = bs->areanum;
  639.         VectorCopy(bs->origin, bs->teamgoal.origin);
  640.         VectorSet(bs->teamgoal.mins, -8, -8, -8);
  641.         VectorSet(bs->teamgoal.maxs, 8, 8, 8);
  642.     }
  643.     else if (match->subtype & ST_HERE) {
  644.         //if this is the bot self
  645.         if (client == bs->client) return;
  646.         //
  647.         bs->teamgoal.entitynum = -1;
  648.         BotEntityInfo(client, &entinfo);
  649.         //if info is valid (in PVS)
  650.         if (entinfo.valid) {
  651.             areanum = BotPointAreaNum(entinfo.origin);
  652.             if (areanum && trap_AAS_AreaReachability(areanum)) {
  653.                 //NOTE: just cheat and assume the bot knows where the person is
  654.                 //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) {
  655.                     bs->teamgoal.entitynum = client;
  656.                     bs->teamgoal.areanum = areanum;
  657.                     VectorCopy(entinfo.origin, bs->teamgoal.origin);
  658.                     VectorSet(bs->teamgoal.mins, -8, -8, -8);
  659.                     VectorSet(bs->teamgoal.maxs, 8, 8, 8);
  660.                 //}
  661.             }
  662.         }
  663.         //if the other is not visible
  664.         if (bs->teamgoal.entitynum < 0) {
  665.             BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);
  666.             trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  667.             return;
  668.         }
  669.     }
  670.     else if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
  671.         //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
  672.         //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  673.         return;
  674.     }
  675.     //set the time to send a message to the team mates
  676.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  677.     //set the ltg type
  678.     bs->ltgtype = LTG_CAMPORDER;
  679.     //get the team goal time
  680.     bs->teamgoal_time = BotGetTime(match);
  681.     //set the team goal time
  682.     if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_CAMP_TIME;
  683.     //the teammate that requested the camping
  684.     bs->teammate = client;
  685.     //not arrived yet
  686.     bs->arrive_time = 0;
  687.     //
  688. #ifdef DEBUG
  689.     BotPrintTeamGoal(bs);
  690. #endif //DEBUG
  691. }
  692.  
  693. /*
  694. ==================
  695. BotMatch_Patrol
  696. ==================
  697. */
  698. void BotMatch_Patrol(bot_state_t *bs, bot_match_t *match) {
  699.     if (!TeamPlayIsOn()) return;
  700.     //if not addressed to this bot
  701.     if (!BotAddressedToBot(bs, match)) return;
  702.     //get the patrol waypoints
  703.     if (!BotGetPatrolWaypoints(bs, match)) return;
  704.     //set the time to send a message to the team mates
  705.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  706.     //set the ltg type
  707.     bs->ltgtype = LTG_PATROL;
  708.     //get the team goal time
  709.     bs->teamgoal_time = BotGetTime(match);
  710.     //set the team goal time if not set already
  711.     if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_PATROL_TIME;
  712.     //
  713. #ifdef DEBUG
  714.     BotPrintTeamGoal(bs);
  715. #endif //DEBUG
  716. }
  717.  
  718. /*
  719. ==================
  720. BotMatch_GetFlag
  721. ==================
  722. */
  723. void BotMatch_GetFlag(bot_state_t *bs, bot_match_t *match) {
  724.     //if not in CTF mode
  725.     if (gametype != GT_CTF || !ctf_redflag.areanum || !ctf_blueflag.areanum) return;
  726.     //if not addressed to this bot
  727.     if (!BotAddressedToBot(bs, match)) return;
  728.     //set the time to send a message to the team mates
  729.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  730.     //set the ltg type
  731.     bs->ltgtype = LTG_GETFLAG;
  732.     //set the team goal time
  733.     bs->teamgoal_time = trap_AAS_Time() + CTF_GETFLAG_TIME;
  734. #ifdef DEBUG
  735.     BotPrintTeamGoal(bs);
  736. #endif //DEBUG
  737. }
  738.  
  739. /*
  740. ==================
  741. BotMatch_RushBase
  742. ==================
  743. */
  744. void BotMatch_RushBase(bot_state_t *bs, bot_match_t *match) {
  745.     //if not in CTF mode
  746.     if (gametype != GT_CTF || !ctf_redflag.areanum || !ctf_blueflag.areanum) return;
  747.     //if not addressed to this bot
  748.     if (!BotAddressedToBot(bs, match)) return;
  749.     //set the time to send a message to the team mates
  750.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  751.     //set the ltg type
  752.     bs->ltgtype = LTG_RUSHBASE;
  753.     //set the team goal time
  754.     bs->teamgoal_time = trap_AAS_Time() + CTF_RUSHBASE_TIME;
  755.     bs->rushbaseaway_time = 0;
  756. #ifdef DEBUG
  757.     BotPrintTeamGoal(bs);
  758. #endif //DEBUG
  759. }
  760.  
  761. /*
  762. ==================
  763. BotMatch_TaskPreference
  764. ==================
  765. */
  766. void BotMatch_TaskPreference(bot_state_t *bs, bot_match_t *match) {
  767.     char netname[MAX_NETNAME];
  768.     char teammatename[MAX_MESSAGE_SIZE];
  769.     int teammate, preference;
  770.  
  771.     ClientName(bs->client, netname, sizeof(netname));
  772.     if (Q_stricmp(netname, bs->teamleader) != 0) return;
  773.  
  774.     trap_BotMatchVariable(match, NETNAME, teammatename, sizeof(teammatename));
  775.     teammate = ClientFromName(teammatename);
  776.     if (teammate < 0) return;
  777.  
  778.     preference = BotGetTeamMateCTFPreference(bs, teammate);
  779.     switch(match->subtype)
  780.     {
  781.         case ST_DEFENDER:
  782.         {
  783.             preference &= ~CTFTP_ATTACKER;
  784.             preference |= CTFTP_DEFENDER;
  785.             break;
  786.         }
  787.         case ST_ATTACKER:
  788.         {
  789.             preference &= ~CTFTP_DEFENDER;
  790.             preference |= CTFTP_ATTACKER;
  791.             break;
  792.         }
  793.         case ST_ROAMER:
  794.         {
  795.             preference &= ~(CTFTP_ATTACKER|CTFTP_DEFENDER);
  796.             break;
  797.         }
  798.     }
  799.     BotSetTeamMateCTFPreference(bs, teammate, preference);
  800.     //
  801.     EasyClientName(teammate, teammatename, sizeof(teammatename));
  802.     BotAI_BotInitialChat(bs, "keepinmind", teammatename, NULL);
  803.     trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  804. }
  805.  
  806. /*
  807. ==================
  808. BotMatch_ReturnFlag
  809. ==================
  810. */
  811. void BotMatch_ReturnFlag(bot_state_t *bs, bot_match_t *match) {
  812.     //if not in CTF mode
  813.     if (gametype != GT_CTF) return;
  814.     //if not addressed to this bot
  815.     if (!BotAddressedToBot(bs, match)) return;
  816.     //set the time to send a message to the team mates
  817.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  818.     //set the ltg type
  819.     bs->ltgtype = LTG_RETURNFLAG;
  820.     //set the team goal time
  821.     bs->teamgoal_time = trap_AAS_Time() + CTF_RETURNFLAG_TIME;
  822.     bs->rushbaseaway_time = 0;
  823. #ifdef DEBUG
  824.     BotPrintTeamGoal(bs);
  825. #endif //DEBUG
  826. }
  827.  
  828. /*
  829. ==================
  830. BotMatch_JoinSubteam
  831. ==================
  832. */
  833. void BotMatch_JoinSubteam(bot_state_t *bs, bot_match_t *match) {
  834.     char teammate[MAX_MESSAGE_SIZE];
  835.  
  836.     if (!TeamPlayIsOn()) return;
  837.     //if not addressed to this bot
  838.     if (!BotAddressedToBot(bs, match)) return;
  839.     //get the sub team name
  840.     trap_BotMatchVariable(match, TEAMNAME, teammate, MAX_MESSAGE_SIZE);
  841.     //set the sub team name
  842.     strncpy(bs->subteam, teammate, 32);
  843.     bs->subteam[31] = '\0';
  844.     //
  845.     BotAI_BotInitialChat(bs, "joinedteam", teammate, NULL);
  846.     trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  847. }
  848.  
  849. /*
  850. ==================
  851. BotMatch_LeaveSubteam
  852. ==================
  853. */
  854. void BotMatch_LeaveSubteam(bot_state_t *bs, bot_match_t *match) {
  855.     if (!TeamPlayIsOn()) return;
  856.     //if not addressed to this bot
  857.     if (!BotAddressedToBot(bs, match)) return;
  858.     //
  859.     if (strlen(bs->subteam))
  860.     {
  861.         BotAI_BotInitialChat(bs, "leftteam", bs->subteam, NULL);
  862.     } //end if
  863.     trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  864.     strcpy(bs->subteam, "");
  865. }
  866.  
  867. /*
  868. ==================
  869. BotMatch_LeaveSubteam
  870. ==================
  871. */
  872. void BotMatch_WhichTeam(bot_state_t *bs, bot_match_t *match) {
  873.     if (!TeamPlayIsOn()) return;
  874.     //if not addressed to this bot
  875.     if (!BotAddressedToBot(bs, match)) return;
  876.     //
  877.     if (strlen(bs->subteam)) {
  878.         BotAI_BotInitialChat(bs, "inteam", bs->subteam, NULL);
  879.     }
  880.     else {
  881.         BotAI_BotInitialChat(bs, "noteam", NULL);
  882.     }
  883.     trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  884. }
  885.  
  886. /*
  887. ==================
  888. BotMatch_CheckPoint
  889. ==================
  890. */
  891. void BotMatch_CheckPoint(bot_state_t *bs, bot_match_t *match) {
  892.     int areanum;
  893.     char buf[MAX_MESSAGE_SIZE];
  894.     vec3_t position;
  895.     bot_waypoint_t *cp;
  896.  
  897.     if (!TeamPlayIsOn()) return;
  898.     //
  899.     trap_BotMatchVariable(match, POSITION, buf, MAX_MESSAGE_SIZE);
  900.     VectorClear(position);
  901.     //BotGPSToPosition(buf, position);
  902.     sscanf(buf, "%f %f %f", &position[0], &position[1], &position[2]);
  903.     position[2] += 0.5;
  904.     areanum = BotPointAreaNum(position);
  905.     if (!areanum) {
  906.         if (BotAddressedToBot(bs, match)) {
  907.             BotAI_BotInitialChat(bs, "checkpoint_invalid", NULL);
  908.             trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  909.         }
  910.         return;
  911.     }
  912.     //
  913.     trap_BotMatchVariable(match, NAME, buf, MAX_MESSAGE_SIZE);
  914.     //check if there already exists a checkpoint with this name
  915.     cp = BotFindWayPoint(bs->checkpoints, buf);
  916.     if (cp) {
  917.         if (cp->next) cp->next->prev = cp->prev;
  918.         if (cp->prev) cp->prev->next = cp->next;
  919.         else bs->checkpoints = cp->next;
  920.         cp->inuse = qfalse;
  921.     }
  922.     //create a new check point
  923.     cp = BotCreateWayPoint(buf, position, areanum);
  924.     //add the check point to the bot's known chech points
  925.     cp->next = bs->checkpoints;
  926.     if (bs->checkpoints) bs->checkpoints->prev = cp;
  927.     bs->checkpoints = cp;
  928.     //
  929.     if (BotAddressedToBot(bs, match)) {
  930.         Com_sprintf(buf, sizeof(buf), "%1.0f %1.0f %1.0f", cp->goal.origin[0],
  931.                                                     cp->goal.origin[1],
  932.                                                     cp->goal.origin[2]);
  933.  
  934.         BotAI_BotInitialChat(bs, "checkpoint_confirm", cp->name, buf, NULL);
  935.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  936.     }
  937. }
  938.  
  939. /*
  940. ==================
  941. BotMatch_FormationSpace
  942. ==================
  943. */
  944. void BotMatch_FormationSpace(bot_state_t *bs, bot_match_t *match) {
  945.     char buf[MAX_MESSAGE_SIZE];
  946.     float space;
  947.  
  948.     if (!TeamPlayIsOn()) return;
  949.     //if not addressed to this bot
  950.     if (!BotAddressedToBot(bs, match)) return;
  951.     //
  952.     trap_BotMatchVariable(match, NUMBER, buf, MAX_MESSAGE_SIZE);
  953.     //if it's the distance in feet
  954.     if (match->subtype & ST_FEET) space = 0.3048 * 32 * atof(buf);
  955.     //else it's in meters
  956.     else space = 32 * atof(buf);
  957.     //check if the formation intervening space is valid
  958.     if (space < 48 || space > 500) space = 100;
  959.     bs->formation_dist = space;
  960. }
  961.  
  962. /*
  963. ==================
  964. BotMatch_Dismiss
  965. ==================
  966. */
  967. void BotMatch_Dismiss(bot_state_t *bs, bot_match_t *match) {
  968.     if (!TeamPlayIsOn()) return;
  969.     //if not addressed to this bot
  970.     if (!BotAddressedToBot(bs, match)) return;
  971.     //
  972.     bs->ltgtype = 0;
  973.     bs->lead_time = 0;
  974.     //
  975.     BotAI_BotInitialChat(bs, "dismissed", NULL);
  976.     trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  977. }
  978.  
  979. /*
  980. ==================
  981. BotMatch_StartTeamLeaderShip
  982. ==================
  983. */
  984. void BotMatch_StartTeamLeaderShip(bot_state_t *bs, bot_match_t *match) {
  985.     int client;
  986.     char teammate[MAX_MESSAGE_SIZE];
  987.  
  988.     if (!TeamPlayIsOn()) return;
  989.     //if chats for him or herself
  990.     if (match->subtype & ST_I) {
  991.         //get the team mate that will be the team leader
  992.         trap_BotMatchVariable(match, NETNAME, teammate, sizeof(teammate));
  993.         strncpy(bs->teamleader, teammate, sizeof(bs->teamleader));
  994.         bs->teamleader[sizeof(bs->teamleader)] = '\0';
  995.     }
  996.     //chats for someone else
  997.     else {
  998.         //get the team mate that will be the team leader
  999.         trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
  1000.         client = FindClientByName(teammate);
  1001.         if (client >= 0) ClientName(client, bs->teamleader, sizeof(bs->teamleader));
  1002.     }
  1003. }
  1004.  
  1005. /*
  1006. ==================
  1007. BotMatch_StopTeamLeaderShip
  1008. ==================
  1009. */
  1010. void BotMatch_StopTeamLeaderShip(bot_state_t *bs, bot_match_t *match) {
  1011.     int client;
  1012.     char teammate[MAX_MESSAGE_SIZE];
  1013.     char netname[MAX_MESSAGE_SIZE];
  1014.  
  1015.     if (!TeamPlayIsOn()) return;
  1016.     //get the team mate that stops being the team leader
  1017.     trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
  1018.     //if chats for him or herself
  1019.     if (match->subtype & ST_I) {
  1020.         trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  1021.         client = FindClientByName(netname);
  1022.     }
  1023.     //chats for someone else
  1024.     else {
  1025.         client = FindClientByName(teammate);
  1026.     } //end else
  1027.     if (client >= 0) {
  1028.         if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) {
  1029.             bs->teamleader[0] = '\0';
  1030.         }
  1031.     }
  1032. }
  1033.  
  1034. /*
  1035. ==================
  1036. BotMatch_WhoIsTeamLeader
  1037. ==================
  1038. */
  1039. void BotMatch_WhoIsTeamLeader(bot_state_t *bs, bot_match_t *match) {
  1040.     char netname[MAX_MESSAGE_SIZE];
  1041.  
  1042.     if (!TeamPlayIsOn()) return;
  1043.  
  1044.     ClientName(bs->client, netname, sizeof(netname));
  1045.     //if this bot IS the team leader
  1046.     if (!Q_stricmp(netname, bs->teamleader)) {
  1047.         trap_EA_SayTeam(bs->client, "I'm the team leader\n");
  1048.     }
  1049. }
  1050.  
  1051. /*
  1052. ==================
  1053. BotMatch_WhatAreYouDoing
  1054. ==================
  1055. */
  1056. void BotMatch_WhatAreYouDoing(bot_state_t *bs, bot_match_t *match) {
  1057.     char netname[MAX_MESSAGE_SIZE];
  1058.     char goalname[MAX_MESSAGE_SIZE];
  1059.  
  1060.     //if not addressed to this bot
  1061.     if (!BotAddressedToBot(bs, match)) return;
  1062.     //
  1063.     switch(bs->ltgtype) {
  1064.         case LTG_TEAMHELP:
  1065.         {
  1066.             EasyClientName(bs->teammate, netname, sizeof(netname));
  1067.             BotAI_BotInitialChat(bs, "helping", netname, NULL);
  1068.             break;
  1069.         }
  1070.         case LTG_TEAMACCOMPANY:
  1071.         {
  1072.             EasyClientName(bs->teammate, netname, sizeof(netname));
  1073.             BotAI_BotInitialChat(bs, "accompanying", netname, NULL);
  1074.             break;
  1075.         }
  1076.         case LTG_DEFENDKEYAREA:
  1077.         {
  1078.             trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
  1079.             BotAI_BotInitialChat(bs, "defending", goalname, NULL);
  1080.             break;
  1081.         }
  1082.         case LTG_GETITEM:
  1083.         {
  1084.             trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
  1085.             BotAI_BotInitialChat(bs, "gettingitem", goalname, NULL);
  1086.             break;
  1087.         }
  1088.         case LTG_KILL:
  1089.         {
  1090.             ClientName(bs->teamgoal.entitynum, netname, sizeof(netname));
  1091.             BotAI_BotInitialChat(bs, "killing", netname, NULL);
  1092.             break;
  1093.         }
  1094.         case LTG_CAMP:
  1095.         case LTG_CAMPORDER:
  1096.         {
  1097.             BotAI_BotInitialChat(bs, "camping", NULL);
  1098.             break;
  1099.         }
  1100.         case LTG_PATROL:
  1101.         {
  1102.             BotAI_BotInitialChat(bs, "patrolling", NULL);
  1103.             break;
  1104.         }
  1105.         case LTG_GETFLAG:
  1106.         {
  1107.             BotAI_BotInitialChat(bs, "capturingflag", NULL);
  1108.             break;
  1109.         }
  1110.         case LTG_RUSHBASE:
  1111.         {
  1112.             BotAI_BotInitialChat(bs, "rushingbase", NULL);
  1113.             break;
  1114.         }
  1115.         case LTG_RETURNFLAG:
  1116.         {
  1117.             BotAI_BotInitialChat(bs, "returningflag", NULL);
  1118.             break;
  1119.         }
  1120.         default:
  1121.         {
  1122.             BotAI_BotInitialChat(bs, "roaming", NULL);
  1123.             break;
  1124.         }
  1125.     }
  1126.     //chat what the bot is doing
  1127.     trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  1128. }
  1129.  
  1130. /*
  1131. ==================
  1132. BotMatch_WhatIsMyCommand
  1133. ==================
  1134. */
  1135. void BotMatch_WhatIsMyCommand(bot_state_t *bs, bot_match_t *match) {
  1136.     char netname[MAX_NETNAME];
  1137.  
  1138.     ClientName(bs->client, netname, sizeof(netname));
  1139.     if (Q_stricmp(netname, bs->teamleader) != 0) return;
  1140.     bs->forceorders = qtrue;
  1141. }
  1142.  
  1143. /*
  1144. ==================
  1145. BotNearestVisibleItem
  1146. ==================
  1147. */
  1148. float BotNearestVisibleItem(bot_state_t *bs, char *itemname, bot_goal_t *goal) {
  1149.     int i;
  1150.     char name[64];
  1151.     bot_goal_t tmpgoal;
  1152.     float dist, bestdist;
  1153.     vec3_t dir;
  1154.     bsp_trace_t trace;
  1155.  
  1156.     bestdist = 999999;
  1157.     i = -1;
  1158.     do {
  1159.         i = trap_BotGetLevelItemGoal(i, itemname, &tmpgoal);
  1160.         trap_BotGoalName(tmpgoal.number, name, sizeof(name));
  1161.         if (Q_stricmp(itemname, name) != 0) continue;
  1162.         VectorSubtract(tmpgoal.origin, bs->origin, dir);
  1163.         dist = VectorLength(dir);
  1164.         if (dist < bestdist) {
  1165.             //trace from start to end
  1166.             BotAI_Trace(&trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
  1167.             if (trace.fraction >= 1.0) {
  1168.                 bestdist = dist;
  1169.                 memcpy(goal, &tmpgoal, sizeof(bot_goal_t));
  1170.             }
  1171.         }
  1172.     } while(i > 0);
  1173.     return bestdist;
  1174. }
  1175.  
  1176. /*
  1177. ==================
  1178. BotMatch_WhereAreYou
  1179. ==================
  1180. */
  1181. void BotMatch_WhereAreYou(bot_state_t *bs, bot_match_t *match) {
  1182.     float dist, bestdist;
  1183.     int i, bestitem, redflagtt, blueflagtt, redtobluett;
  1184.     bot_goal_t goal;
  1185.     char *nearbyitems[] = {
  1186.         "Shotgun",
  1187.         "Grenade Launcher",
  1188.         "Rocket Launcher",
  1189.         "Plasmagun",
  1190.         "Railgun",
  1191.         "Lightning Gun",
  1192.         "BFG10K",
  1193.         "Quad Damage",
  1194.         "Regeneration",
  1195.         "Battle Suit",
  1196.         "Speed",
  1197.         "Invisibility",
  1198.         "Flight",
  1199.         "Armor",
  1200.         "Heavy Armor",
  1201.         "Red Flag",
  1202.         "Blue Flag",
  1203.         NULL
  1204.     };
  1205.     //
  1206.     if (!TeamPlayIsOn()) return;
  1207.     //if not addressed to this bot
  1208.     if (!BotAddressedToBot(bs, match)) return;
  1209.  
  1210.     bestitem = -1;
  1211.     bestdist = 999999;
  1212.     for (i = 0; nearbyitems[i]; i++) {
  1213.         dist = BotNearestVisibleItem(bs, nearbyitems[i], &goal);
  1214.         if (dist < bestdist) {
  1215.             bestdist = dist;
  1216.             bestitem = i;
  1217.         }
  1218.     }
  1219.     if (bestitem != -1) {
  1220.         if (gametype == GT_CTF) {
  1221.             redflagtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT);
  1222.             blueflagtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT);
  1223.             redtobluett = trap_AAS_AreaTravelTimeToGoalArea(ctf_redflag.areanum, ctf_redflag.origin, ctf_blueflag.areanum, TFL_DEFAULT);
  1224.             if (redflagtt < (redflagtt + blueflagtt) * 0.4) {
  1225.                 BotAI_BotInitialChat(bs, "ctflocation", nearbyitems[bestitem], "red", NULL);
  1226.             }
  1227.             else if (blueflagtt < (redflagtt + blueflagtt) * 0.4) {
  1228.                 BotAI_BotInitialChat(bs, "ctflocation", nearbyitems[bestitem], "blue", NULL);
  1229.             }
  1230.             else {
  1231.                 BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL);
  1232.             }
  1233.         }
  1234.         else {
  1235.             BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL);
  1236.         }
  1237.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  1238.     }
  1239. }
  1240.  
  1241. /*
  1242. ==================
  1243. BotMatch_LeadTheWay
  1244. ==================
  1245. */
  1246. void BotMatch_LeadTheWay(bot_state_t *bs, bot_match_t *match) {
  1247.     aas_entityinfo_t entinfo;
  1248.     char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE];
  1249.     int client, areanum, other;
  1250.  
  1251.     if (!TeamPlayIsOn()) return;
  1252.     //if not addressed to this bot
  1253.     if (!BotAddressedToBot(bs, match)) return;
  1254.     //if someone asks for someone else
  1255.     if (match->subtype & ST_SOMEONE) {
  1256.         //get the team mate name
  1257.         trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
  1258.         client = FindClientByName(teammate);
  1259.         //if this is the bot self
  1260.         if (client == bs->client) {
  1261.             other = qfalse;
  1262.         }
  1263.         else if (!BotSameTeam(bs, client)) {
  1264.             //FIXME: say "I don't help the enemy"
  1265.             return;
  1266.         }
  1267.         else {
  1268.             other = qtrue;
  1269.         }
  1270.     }
  1271.     else {
  1272.         //get the netname
  1273.         trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  1274.         client = ClientFromName(netname);
  1275.         other = qfalse;
  1276.     }
  1277.     //if the bot doesn't know who to help (FindClientByName returned -1)
  1278.     if (client < 0) {
  1279.         BotAI_BotInitialChat(bs, "whois", netname, NULL);
  1280.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  1281.         return;
  1282.     }
  1283.     //
  1284.     bs->lead_teamgoal.entitynum = -1;
  1285.     BotEntityInfo(client, &entinfo);
  1286.     //if info is valid (in PVS)
  1287.     if (entinfo.valid) {
  1288.         areanum = BotPointAreaNum(entinfo.origin);
  1289.         if (areanum && trap_AAS_AreaReachability(areanum)) {
  1290.             bs->lead_teamgoal.entitynum = client;
  1291.             bs->lead_teamgoal.areanum = areanum;
  1292.             VectorCopy(entinfo.origin, bs->lead_teamgoal.origin);
  1293.             VectorSet(bs->lead_teamgoal.mins, -8, -8, -8);
  1294.             VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8);
  1295.         }
  1296.     }
  1297.  
  1298.     if (bs->teamgoal.entitynum < 0) {
  1299.         if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL);
  1300.         else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);
  1301.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  1302.         return;
  1303.     }
  1304.     bs->lead_teammate = client;
  1305.     bs->lead_time = trap_AAS_Time() + TEAM_LEAD_TIME;
  1306.     bs->leadvisible_time = 0;
  1307.     bs->leadmessage_time = -(trap_AAS_Time() + 2 * random());
  1308. }
  1309.  
  1310. /*
  1311. ==================
  1312. BotMatch_Kill
  1313. ==================
  1314. */
  1315. void BotMatch_Kill(bot_state_t *bs, bot_match_t *match) {
  1316.     char enemy[MAX_MESSAGE_SIZE];
  1317.     int client;
  1318.  
  1319.     if (!TeamPlayIsOn()) return;
  1320.     //if not addressed to this bot
  1321.     if (!BotAddressedToBot(bs, match)) return;
  1322.  
  1323.     trap_BotMatchVariable(match, ENEMY, enemy, sizeof(enemy));
  1324.     //
  1325.     client = FindEnemyByName(bs, enemy);
  1326.     if (client < 0) {
  1327.         BotAI_BotInitialChat(bs, "whois", enemy, NULL);
  1328.         trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
  1329.         return;
  1330.     }
  1331.     bs->teamgoal.entitynum = client;
  1332.     //set the time to send a message to the team mates
  1333.     bs->teammessage_time = trap_AAS_Time() + 2 * random();
  1334.     //set the ltg type
  1335.     bs->ltgtype = LTG_KILL;
  1336.     //set the team goal time
  1337.     bs->teamgoal_time = trap_AAS_Time() + TEAM_KILL_SOMEONE;
  1338. #ifdef DEBUG
  1339.     BotPrintTeamGoal(bs);
  1340. #endif //DEBUG
  1341. }
  1342.  
  1343. /*
  1344. ==================
  1345. BotMatch_CTF
  1346. ==================
  1347. */
  1348. void BotMatch_CTF(bot_state_t *bs, bot_match_t *match) {
  1349.  
  1350.     char flag[128], netname[MAX_NETNAME];
  1351.  
  1352.     trap_BotMatchVariable(match, FLAG, flag, sizeof(flag));
  1353.     if (match->subtype & ST_GOTFLAG) {
  1354.         if (!Q_stricmp(flag, "red")) {
  1355.             bs->redflagstatus = 1;
  1356.             if (BotCTFTeam(bs) == CTF_TEAM_BLUE) {
  1357.                 trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  1358.                 bs->flagcarrier = ClientFromName(netname);
  1359.             }
  1360.         }
  1361.         else {
  1362.             bs->blueflagstatus = 1;
  1363.             if (BotCTFTeam(bs) == CTF_TEAM_RED) {
  1364.                 trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
  1365.                 bs->flagcarrier = ClientFromName(netname);
  1366.             }
  1367.         }
  1368.         bs->flagstatuschanged = 1;
  1369.         bs->lastflagcapture_time = trap_AAS_Time();
  1370.     }
  1371.     else if (match->subtype & ST_CAPTUREDFLAG) {
  1372.         bs->redflagstatus = 0;
  1373.         bs->blueflagstatus = 0;
  1374.         bs->flagcarrier = 0;
  1375.         bs->flagstatuschanged = 1;
  1376.     }
  1377.     else if (match->subtype & ST_RETURNEDFLAG) {
  1378.         if (!Q_stricmp(flag, "red")) bs->redflagstatus = 0;
  1379.         else bs->blueflagstatus = 0;
  1380.         bs->flagstatuschanged = 1;
  1381.     }
  1382. }
  1383.  
  1384. /*
  1385. ==================
  1386. BotMatchMessage
  1387. ==================
  1388. */
  1389. int BotMatchMessage(bot_state_t *bs, char *message) {
  1390.     bot_match_t match;
  1391.  
  1392.     match.type = 0;
  1393.     //if it is an unknown message
  1394.     if (!trap_BotFindMatch(message, &match, MTCONTEXT_ENTERGAME
  1395.                                             |MTCONTEXT_INITIALTEAMCHAT
  1396.                                             |MTCONTEXT_CTF)) {
  1397.         return qfalse;
  1398.     }
  1399.     //react to the found message
  1400.     switch(match.type)
  1401.     {
  1402.         case MSG_HELP:                    //someone calling for help
  1403.         case MSG_ACCOMPANY:                //someone calling for company
  1404.         {
  1405.             BotMatch_HelpAccompany(bs, &match);
  1406.             break;
  1407.         }
  1408.         case MSG_DEFENDKEYAREA:            //teamplay defend a key area
  1409.         {
  1410.             BotMatch_DefendKeyArea(bs, &match);
  1411.             break;
  1412.         }
  1413.         case MSG_CAMP:                    //camp somewhere
  1414.         {
  1415.             BotMatch_Camp(bs, &match);
  1416.             break;
  1417.         }
  1418.         case MSG_PATROL:                //patrol between several key areas
  1419.         {
  1420.             BotMatch_Patrol(bs, &match);
  1421.             break;
  1422.         }
  1423. #ifdef CTF
  1424.         case MSG_GETFLAG:                //ctf get the enemy flag
  1425.         {
  1426.             BotMatch_GetFlag(bs, &match);
  1427.             break;
  1428.         }
  1429.         case MSG_RUSHBASE:                //ctf rush to the base
  1430.         {
  1431.             BotMatch_RushBase(bs, &match);
  1432.             break;
  1433.         }
  1434.         case MSG_RETURNFLAG:
  1435.         {
  1436.             BotMatch_ReturnFlag(bs, &match);
  1437.             break;
  1438.         }
  1439.         case MSG_CTFTASKPREFERENCE:
  1440.         {
  1441.             BotMatch_TaskPreference(bs, &match);
  1442.             break;
  1443.         }
  1444.         case MSG_CTF:
  1445.         {
  1446.             BotMatch_CTF(bs, &match);
  1447.             break;
  1448.         }
  1449. #endif //CTF
  1450.         case MSG_GETITEM:
  1451.         {
  1452.             BotMatch_GetItem(bs, &match);
  1453.             break;
  1454.         }
  1455.         case MSG_JOINSUBTEAM:            //join a sub team
  1456.         {
  1457.             BotMatch_JoinSubteam(bs, &match);
  1458.             break;
  1459.         }
  1460.         case MSG_LEAVESUBTEAM:            //leave a sub team
  1461.         {
  1462.             BotMatch_LeaveSubteam(bs, &match);
  1463.             break;
  1464.         }
  1465.         case MSG_WHICHTEAM:
  1466.         {
  1467.             BotMatch_WhichTeam(bs, &match);
  1468.             break;
  1469.         }
  1470.         case MSG_CHECKPOINT:            //remember a check point
  1471.         {
  1472.             BotMatch_CheckPoint(bs, &match);
  1473.             break;
  1474.         }
  1475.         case MSG_CREATENEWFORMATION:    //start the creation of a new formation
  1476.         {
  1477.             trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged");
  1478.             break;
  1479.         }
  1480.         case MSG_FORMATIONPOSITION:        //tell someone his/her position in the formation
  1481.         {
  1482.             trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged");
  1483.             break;
  1484.         }
  1485.         case MSG_FORMATIONSPACE:        //set the formation space
  1486.         {
  1487.             BotMatch_FormationSpace(bs, &match);
  1488.             break;
  1489.         }
  1490.         case MSG_DOFORMATION:            //form a certain formation
  1491.         {
  1492.             break;
  1493.         }
  1494.         case MSG_DISMISS:                //dismiss someone
  1495.         {
  1496.             BotMatch_Dismiss(bs, &match);
  1497.             break;
  1498.         }
  1499.         case MSG_STARTTEAMLEADERSHIP:    //someone will become the team leader
  1500.         {
  1501.             BotMatch_StartTeamLeaderShip(bs, &match);
  1502.             break;
  1503.         }
  1504.         case MSG_STOPTEAMLEADERSHIP:    //someone will stop being the team leader
  1505.         {
  1506.             BotMatch_StopTeamLeaderShip(bs, &match);
  1507.             break;
  1508.         }
  1509.         case MSG_WHOISTEAMLAEDER:
  1510.         {
  1511.             BotMatch_WhoIsTeamLeader(bs, &match);
  1512.             break;
  1513.         }
  1514.         case MSG_WHATAREYOUDOING:        //ask a bot what he/she is doing
  1515.         {
  1516.             BotMatch_WhatAreYouDoing(bs, &match);
  1517.             break;
  1518.         }
  1519.         case MSG_WHATISMYCOMMAND:
  1520.         {
  1521.             BotMatch_WhatIsMyCommand(bs, &match);
  1522.             break;
  1523.         }
  1524.         case MSG_WHEREAREYOU:
  1525.         {
  1526.             BotMatch_WhereAreYou(bs, &match);
  1527.             break;
  1528.         }
  1529.         case MSG_LEADTHEWAY:
  1530.         {
  1531.             BotMatch_LeadTheWay(bs, &match);
  1532.             break;
  1533.         }
  1534.         case MSG_KILL:
  1535.         {
  1536.             BotMatch_Kill(bs, &match);
  1537.             break;
  1538.         }
  1539.         case MSG_ENTERGAME:                //someone entered the game
  1540.         {
  1541.             //NOTE: eliza chats will catch this
  1542.             //BotMatchVariable(&match, NETNAME, netname);
  1543.             //Com_sprintf(buf, sizeof(buf), "heya %s", netname);
  1544.             //EA_Say(bs->client, buf);
  1545.             break;
  1546.         }
  1547.         case MSG_WAIT:
  1548.         {
  1549.             break;
  1550.         }
  1551.         default:
  1552.         {
  1553.             BotAI_Print(PRT_MESSAGE, "unknown match type\n");
  1554.             break;
  1555.         }
  1556.     }
  1557.     return qtrue;
  1558. }
  1559.